﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;


namespace KamLib
{


    public static class CollectionHelper
    {
        #region Array

        [DebuggerStepThrough]
        public static bool yes<T>(this T[] array)
        {
            return array != null && array.Length > 0;
        }


        [DebuggerStepThrough]
        public static bool no<T>(this T[] array)
        {
            return array == null || array.Length == 0;
        }


        [DebuggerStepThrough]
        public static bool yes<T>(this IList<T> array)
        {
            return array != null && array.Count > 0;
        }


        [DebuggerStepThrough]
        public static bool no<T>(this IList<T> array)
        {
            return array == null || array.Count == 0;
        }

        #endregion


        #region Collection

        public static void AddRange<T>(this ICollection<T> me, IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                me.Add(item);
            }
        }


        [DebuggerStepThrough]
        public static void RemoveRange<TItem>(this ICollection<TItem> me, IEnumerable<TItem> items) where TItem : class
        {
            if (me == null || items == null) return;

            foreach (var item in items)
            {
                me.Remove(item);
            }
        }


        [DebuggerStepThrough]
        public static ObservableCollection<TItem> OnAdd<TItem>(this ObservableCollection<TItem> me, Action<TItem> onAdd)
            where TItem : class
        {
            if (me == null || onAdd == null) return me;

            me.CollectionChanged += (sender, args) =>
            {
                if (args.Action != NotifyCollectionChangedAction.Add) return;
                foreach (TItem newItem in args.NewItems)
                {
                    onAdd(newItem);
                }
            };

            return me;
        }


        [DebuggerStepThrough]
        public static ObservableCollection<TItem> OnAdd<TItem>(this ObservableCollection<TItem> me, Action<TItem, NotifyCollectionChangedEventArgs> onAdd)
            where TItem : class
        {
            if (me == null || onAdd == null) return me;

            me.CollectionChanged += (sender, args) =>
            {
                if (args.Action != NotifyCollectionChangedAction.Add) return;
                foreach (TItem newItem in args.NewItems)
                {
                    onAdd(newItem, args);
                }
            };

            return me;
        }


        [DebuggerStepThrough]
        public static ObservableCollection<TItem> OnRemove<TItem>(this ObservableCollection<TItem> me, Action<TItem> onRemove)
            where TItem : class
        {
            if (me == null || onRemove == null) return me;

            me.CollectionChanged += (sender, args) =>
            {
                if (args.Action != NotifyCollectionChangedAction.Remove) return;
                foreach (TItem oldItems in args.OldItems)
                {
                    onRemove(oldItems);
                }
            };

            return me;
        }


        [DebuggerStepThrough]
        public static ObservableCollection<TItem> OnRemove<TItem>(this ObservableCollection<TItem> me, Action<TItem, NotifyCollectionChangedEventArgs> onRemove)
            where TItem : class
        {
            if (me == null || onRemove == null) return me;

            me.CollectionChanged += (sender, args) =>
            {
                if (args.Action != NotifyCollectionChangedAction.Remove) return;
                foreach (TItem oldItems in args.OldItems)
                {
                    onRemove(oldItems, args);
                }
            };

            return me;
        }

        #endregion


        #region Dictionary

        /// <summary>
        /// Возвращает Dictionary со списком свойств объекта заданного типа.
        /// Предварительно преобразует с помощью convert
        /// </summary>
        [DebuggerStepThrough]
        public static TValue Try<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key)
        {
            TValue value;
            return dic != null && dic.TryGetValue(key, out value) ? value : default(TValue);
        }


        [DebuggerStepThrough]
        public static TValue Try<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key)
        {
            TValue value;
            return dic != null && dic.TryGetValue(key, out value) ? value : default(TValue);
        }


        [DebuggerStepThrough]
        public static TResult Try<TKey, TValue, TResult>(this Dictionary<TKey, TValue> dic, TKey key)
            where TValue : class
        {
            TValue value;
            return dic.TryGetValue(key, out value) && value is TResult ? (TResult) (object) value : default(TResult);
        }


        [DebuggerStepThrough]
        public static TResult Try<TKey, TValue, TResult>(this IDictionary<TKey, TValue> dic, TKey key)
            where TValue : class
        {
            TValue value;
            return dic.TryGetValue(key, out value) && value is TResult ? (TResult) (object) value : default(TResult);
        }


        [DebuggerStepThrough]
        public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> me, IDictionary<TKey, TValue> src, bool replace)
        {
            if (src == null) return me;
            foreach (var p in src)
            {
                if (!me.ContainsKey(p.Key))
                    me.Add(p.Key, p.Value);
                else if (replace)
                    me[p.Key] = p.Value;
            }

            return me;
        }


        [DebuggerStepThrough]
        public static IDictionary<TKey, TValue> AddRange<TKey, TValue>(this IDictionary<TKey, TValue> me, IDictionary<TKey, TValue> src, bool replace)
        {
            if (src == null) return me;
            foreach (var p in src)
            {
                if (!me.ContainsKey(p.Key))
                    me.Add(p.Key, p.Value);
                else if (replace)
                    me[p.Key] = p.Value;
            }

            return me;
        }

        #endregion


        #region List

        [DebuggerStepThrough]
        public static int IndexOf<TItem>(this IList<TItem> list, Func<TItem, bool> match)
        {
            if (list == null || match == null) return -1;

            var len = list.Count;
            for (var i = 0; i < len; ++i)
                if (match(list[i])) return i;
            return -1;
        }


        [DebuggerStepThrough]
        public static TItem Register<TItem>(this IList<TItem> me, TItem item)
        {
            if (me == null) return default(TItem);
            if (!me.Contains(item))
                me.Add(item);
            return item;
        }


        [DebuggerStepThrough]
        public static void Register<TItem>(this IList<TItem> me, IEnumerable<TItem> items)
        {
            if (me == null) return;
            foreach (var item in items)
            {
                if (!me.Contains(item)) me.Add(item);
            }
        }


        /// <summary>
        /// добавляет уникальный элемент в список
        /// </summary>
        /// <param name="item"></param>
        /// <param name="match">поиск существующего элемента</param>
        /// <param name="me"></param>
        /// <returns>возвращает новый или уже существующий элемент</returns>
        [DebuggerStepThrough]
        public static TItem Register<TItem>(this IList<TItem> me, TItem item, Func<TItem, bool> match) where TItem : class
        {
            if (me == null) return default(TItem);
            var itm = me.FirstOrDefault(match);
            if (itm != null) return itm;
            me.Add(item);
            return item;
        }


        [DebuggerStepThrough]
        public static void AddRange<TItem>(this IList<TItem> me, IEnumerable<TItem> items)
        {
            if (me == null || items == null) return;

            foreach (var item in items)
            {
                me.Add(item);
            }
        }


        [DebuggerStepThrough]
        public static void AddRange(this IList me, IEnumerable items)
        {
            if (me == null || items == null) return;

            foreach (var item in items)
            {
                me.Add(item);
            }
        }


        [DebuggerStepThrough]
        public static void RemoveRange<TItem>(this IList<TItem> me, IEnumerable<TItem> items)
        {
            if (me == null || items == null) return;

            foreach (var item in items)
            {
                me.Remove(item);
            }
        }


        [DebuggerStepThrough]
        public static void RemoveRange(this IList me, IEnumerable items)
        {
            if (me == null || items == null) return;

            foreach (var item in items)
            {
                me.Remove(item);
            }
        }

        #endregion


        #region Linq

        public static IEnumerable<TSource> SafeConcat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
        {
            return first != null && second != null ? first.Concat(second) : first ?? second ?? new TSource[] {};
        }

        #endregion
    }


}